"""
Java 符号提取器

支持两种提取模式：
1. AST模式：使用 javalang 库进行精确的语法树解析（推荐）
2. 正则模式：使用正则表达式进行快速但不太精确的提取（回退方案）
"""

import re
from typing import Dict, List, Optional
import subprocess
import sys

from core.extractors_module.base_extractor import LanguageExtractor
from core.interfaces_module.extractor import SymbolInfo

# 尝试导入 javalang 库
try:
    import javalang
    HAS_JAVALANG = True
except ImportError:
    HAS_JAVALANG = False


class JavaExtractor(LanguageExtractor):
    """
    Java 符号提取器

    支持:
    - 类 (class, abstract class, enum)
    - 接口 (interface)
    - 方法 (public, private, protected, static)
    - 字段 (fields, constants)
    - 内部类和匿名类
    - 注解 (annotations)
    """

    def get_language_name(self) -> str:
        """获取语言名称"""
        return "java"

    def get_compiler_command(self) -> str:
        """获取编译器命令"""
        return "javac"

    def get_file_extensions(self) -> List[str]:
        """获取文件扩展名"""
        return ['.java']

    def _should_use_ast(self) -> bool:
        """
        判断是否应该使用 AST 提取

        Returns:
            如果可以且应该使用 AST 返回 True
        """
        # 检查是否有 javalang 库
        if not HAS_JAVALANG:
            if self.config and self.config.analysis.require_ast:
                raise RuntimeError(
                    "需要 AST 提取但 javalang 库未安装。"
                    "请运行: pip install javalang"
                )
            return False

        # 如果配置要求 AST 或没有明确禁用，则使用 AST
        if self.config:
            if self.config.analysis.require_ast:
                return True
            if not self.config.analysis.allow_fallback:
                return True

        return HAS_JAVALANG  # 如果有库就用

    def extract_symbols_from_file(self, file_path: str) -> Dict:
        """
        从单个 Java 文件提取符号

        优先使用 AST 提取，失败时降级到正则提取

        Args:
            file_path: 文件路径

        Returns:
            符号字典
        """
        # 尝试使用 AST 提取
        if self._should_use_ast():
            try:
                return self._extract_java_ast(file_path)
            except Exception as e:
                if self.config and self.config.analysis.log_fallback:
                    self.logger.log_warning(
                        f"Java AST 提取失败，降级到正则模式: {file_path}\n原因: {e}"
                    )
                if self.config and not self.config.analysis.allow_fallback:
                    raise

        # 降级到正则提取
        return self._extract_java_regex(file_path)

    def _extract_java_ast(self, file_path: str) -> Dict:
        """
        使用 javalang 库进行 AST 提取

        Args:
            file_path: Java 文件路径

        Returns:
            符号字典
        """
        symbols = {
            'classes': {},
            'interfaces': {},
            'enums': {},
            'methods': {},
            'fields': {},
            'annotations': {},
            'file_symbols': {file_path: []}
        }

        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()

        # 解析 Java 代码为 AST
        tree = javalang.parse.parse(content)

        # 遍历 AST 节点
        for path, node in tree:
            if isinstance(node, javalang.tree.ClassDeclaration):
                # 处理类声明
                self._process_class_declaration(node, file_path, symbols)

            elif isinstance(node, javalang.tree.InterfaceDeclaration):
                # 处理接口声明
                self._process_interface_declaration(node, file_path, symbols)

            elif isinstance(node, javalang.tree.EnumDeclaration):
                # 处理枚举声明
                self._process_enum_declaration(node, file_path, symbols)

            elif isinstance(node, javalang.tree.MethodDeclaration):
                # 处理方法声明
                self._process_method_declaration(node, file_path, symbols, path)

            elif isinstance(node, javalang.tree.FieldDeclaration):
                # 处理字段声明
                self._process_field_declaration(node, file_path, symbols)

            elif isinstance(node, javalang.tree.AnnotationDeclaration):
                # 处理注解声明
                self._process_annotation_declaration(node, file_path, symbols)

        return symbols

    def _process_class_declaration(self, node, file_path: str, symbols: Dict):
        """处理类声明节点"""
        class_name = node.name
        line_number = node.position.line if node.position else 0
        column = node.position.column if node.position else 0

        # 判断类的类型
        class_type = 'class'
        if node.modifiers and 'abstract' in node.modifiers:
            class_type = 'abstract_class'

        symbols['classes'][class_name] = SymbolInfo(
            name=class_name,
            type=class_type,
            file=file_path,
            line=line_number,
            column=column
        )
        symbols['file_symbols'][file_path].append(class_name)

    def _process_interface_declaration(self, node, file_path: str, symbols: Dict):
        """处理接口声明节点"""
        interface_name = node.name
        line_number = node.position.line if node.position else 0
        column = node.position.column if node.position else 0

        symbols['interfaces'][interface_name] = SymbolInfo(
            name=interface_name,
            type='interface',
            file=file_path,
            line=line_number,
            column=column
        )
        symbols['file_symbols'][file_path].append(interface_name)

    def _process_enum_declaration(self, node, file_path: str, symbols: Dict):
        """处理枚举声明节点"""
        enum_name = node.name
        line_number = node.position.line if node.position else 0
        column = node.position.column if node.position else 0

        symbols['enums'][enum_name] = SymbolInfo(
            name=enum_name,
            type='enum',
            file=file_path,
            line=line_number,
            column=column
        )
        symbols['file_symbols'][file_path].append(enum_name)

    def _process_method_declaration(self, node, file_path: str, symbols: Dict, path):
        """处理方法声明节点"""
        method_name = node.name

        # 跳过构造函数（它们的名字与类名相同）
        if path and len(path) > 1:
            parent = path[-2]
            if isinstance(parent, (javalang.tree.ClassDeclaration,
                                 javalang.tree.EnumDeclaration)) and parent.name == method_name:
                return  # 跳过构造函数

        line_number = node.position.line if node.position else 0
        column = node.position.column if node.position else 0

        # 判断方法类型
        method_type = 'method'
        if node.modifiers:
            if 'static' in node.modifiers:
                method_type = 'static_method'
            elif 'abstract' in node.modifiers:
                method_type = 'abstract_method'

        symbols['methods'][method_name] = SymbolInfo(
            name=method_name,
            type=method_type,
            file=file_path,
            line=line_number,
            column=column
        )

    def _process_field_declaration(self, node, file_path: str, symbols: Dict):
        """处理字段声明节点"""
        # 一个字段声明可能包含多个变量
        for declarator in node.declarators:
            field_name = declarator.name
            line_number = node.position.line if node.position else 0
            column = node.position.column if node.position else 0

            # 判断字段类型
            field_type = 'field'
            if node.modifiers:
                if 'static' in node.modifiers and 'final' in node.modifiers:
                    field_type = 'constant'
                elif 'static' in node.modifiers:
                    field_type = 'static_field'

            symbols['fields'][field_name] = SymbolInfo(
                name=field_name,
                type=field_type,
                file=file_path,
                line=line_number,
                column=column
            )

    def _process_annotation_declaration(self, node, file_path: str, symbols: Dict):
        """处理注解声明节点"""
        annotation_name = node.name
        line_number = node.position.line if node.position else 0
        column = node.position.column if node.position else 0

        symbols['annotations'][annotation_name] = SymbolInfo(
            name=annotation_name,
            type='annotation',
            file=file_path,
            line=line_number,
            column=column
        )
        symbols['file_symbols'][file_path].append(annotation_name)

    def _extract_java_regex(self, file_path: str) -> Dict:
        """
        使用正则表达式提取 Java 符号（回退方案）

        Args:
            file_path: 文件路径

        Returns:
            符号字典
        """
        symbols = {
            'classes': {},
            'interfaces': {},
            'enums': {},
            'methods': {},
            'fields': {},
            'annotations': {},
            'file_symbols': {file_path: []}
        }

        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read()

            # 移除注释以避免误匹配
            content = self._remove_comments(content)

            # 提取类声明（包括内部类）
            class_pattern = r'(?:public\s+|private\s+|protected\s+)?(?:static\s+)?(?:abstract\s+|final\s+)?class\s+(\w+)'
            for match in re.finditer(class_pattern, content):
                class_name = match.group(1)
                line_number = content[:match.start()].count('\n') + 1

                symbols['classes'][class_name] = SymbolInfo(
                    name=class_name,
                    type='class',
                    file=file_path,
                    line=line_number,
                    column=0
                )
                symbols['file_symbols'][file_path].append(class_name)

            # 提取接口声明
            interface_pattern = r'(?:public\s+|private\s+|protected\s+)?interface\s+(\w+)'
            for match in re.finditer(interface_pattern, content):
                interface_name = match.group(1)
                line_number = content[:match.start()].count('\n') + 1

                symbols['interfaces'][interface_name] = SymbolInfo(
                    name=interface_name,
                    type='interface',
                    file=file_path,
                    line=line_number,
                    column=0
                )
                symbols['file_symbols'][file_path].append(interface_name)

            # 提取枚举声明
            enum_pattern = r'(?:public\s+|private\s+|protected\s+)?enum\s+(\w+)'
            for match in re.finditer(enum_pattern, content):
                enum_name = match.group(1)
                line_number = content[:match.start()].count('\n') + 1

                symbols['enums'][enum_name] = SymbolInfo(
                    name=enum_name,
                    type='enum',
                    file=file_path,
                    line=line_number,
                    column=0
                )
                symbols['file_symbols'][file_path].append(enum_name)

            # 提取注解声明
            annotation_pattern = r'@interface\s+(\w+)'
            for match in re.finditer(annotation_pattern, content):
                annotation_name = match.group(1)
                line_number = content[:match.start()].count('\n') + 1

                symbols['annotations'][annotation_name] = SymbolInfo(
                    name=annotation_name,
                    type='annotation',
                    file=file_path,
                    line=line_number,
                    column=0
                )
                symbols['file_symbols'][file_path].append(annotation_name)

            # 提取方法声明（改进的正则，支持泛型返回类型）
            method_pattern = r'(?:public\s+|private\s+|protected\s+)?(?:static\s+)?(?:final\s+)?(?:synchronized\s+)?(?:<[^>]+>\s+)?[\w<>\[\]]+\s+(\w+)\s*\([^)]*\)\s*(?:throws\s+[\w\s,]+)?\s*\{'
            for match in re.finditer(method_pattern, content):
                method_name = match.group(1)
                # 排除关键字
                if method_name not in ['if', 'for', 'while', 'switch', 'catch', 'try', 'class', 'interface', 'enum']:
                    line_number = content[:match.start()].count('\n') + 1

                    symbols['methods'][method_name] = SymbolInfo(
                        name=method_name,
                        type='method',
                        file=file_path,
                        line=line_number,
                        column=0
                    )

            # 提取字段声明（改进的正则，支持泛型）
            field_pattern = r'(?:public\s+|private\s+|protected\s+)?(?:static\s+)?(?:final\s+)?(?:volatile\s+)?[\w<>\[\]]+\s+(\w+)\s*[;=]'
            for match in re.finditer(field_pattern, content):
                field_name = match.group(1)
                # 排除常见的类型名
                if field_name not in ['String', 'int', 'long', 'double', 'float', 'boolean', 'char', 'byte', 'short', 'void']:
                    line_number = content[:match.start()].count('\n') + 1

                    symbols['fields'][field_name] = SymbolInfo(
                        name=field_name,
                        type='field',
                        file=file_path,
                        line=line_number,
                        column=0
                    )

        except Exception as e:
            self.logger.log_error(f"解析 Java 文件失败 {file_path}: {e}")

        return symbols

    def _remove_comments(self, content: str) -> str:
        """
        移除 Java 代码中的注释

        Args:
            content: Java 源代码

        Returns:
            移除注释后的代码
        """
        # 移除单行注释
        content = re.sub(r'//.*?$', '', content, flags=re.MULTILINE)
        # 移除多行注释
        content = re.sub(r'/\*.*?\*/', '', content, flags=re.DOTALL)
        return content

    def extract_symbols_batch(self, files: List[str]) -> Dict:
        """
        批量提取 Java 符号

        优先使用 AST 模式，必要时降级到正则模式

        Args:
            files: Java 文件列表

        Returns:
            合并后的符号字典
        """
        if not files:
            return {}

        mode = "AST" if self._should_use_ast() else "正则"
        self.logger.log_operation(f"Java 分析 ({mode}模式)", f"处理 {len(files)} 个文件")

        symbols = {
            'classes': {},
            'interfaces': {},
            'enums': {},
            'methods': {},
            'fields': {},
            'annotations': {},
            'file_symbols': {}
        }

        success_count = 0
        failed_files = []

        for file_path in files:
            try:
                file_symbols = self.extract_symbols_from_file(file_path)
                self._merge_symbols(symbols, file_symbols)
                success_count += 1
            except Exception as e:
                failed_files.append(file_path)
                self.logger.log_error(f"处理文件失败 {file_path}: {e}")

        if failed_files and self.config and self.config.analysis.log_fallback:
            self.logger.log_warning(
                f"Java 提取完成：成功 {success_count}/{len(files)} 个文件\n"
                f"失败文件: {', '.join(failed_files[:5])}..."
            )

        return symbols

    def _merge_symbols(self, target: Dict, source: Dict):
        """
        合并符号字典

        Args:
            target: 目标字典
            source: 源字典
        """
        for key in source:
            if key == 'file_symbols':
                target[key].update(source[key])
            elif key in target:
                target[key].update(source[key])
            else:
                target[key] = source[key]